using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using PoolGame2.Menus;
using PoolGame2.Assets;
using PoolGame2.Physics;
using PoolGame2.Game;
using PoolGame2.Networking;
using PoolGame2.Player;
using PoolGame2.DebugGame;
using PoolGame2.Managers;
using PoolGame2.Controls;


namespace PoolGame2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class PoolGame : Microsoft.Xna.Framework.Game
    {

        public static AudioDatabase audioDBManger;
        public static GameControls gameControls;
        
        public GraphicsDeviceManager graphics;
        public GraphicsDevice device;
        public SpriteBatch spriteBatch;

        public MenuManager menuManager;
        public NetworkManager networkManager;
        public ManagerGame gameManager = null;
        public ManagerGameEvent eventManager;

        public ModelDatabase modelDatabase;
        public TextureDatabase textureDatabase;
        public FontDatabase fontDatabase;

        public GameScreenOverlay screenOverlay;

        public List<PlayerPresence> signedInPlayers = new List<PlayerPresence>(1);

        public PoolGame()
        {
            graphics = new GraphicsDeviceManager(this);
#if XBOX
            InitGraphicsMode(1280, 720, true);
#else
            InitGraphicsMode(1280, 720, false);
#endif
            graphics.PreferMultiSampling = true;
            graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            Content.RootDirectory = "Content";
            Components.Add(new GamerServicesComponent(this));
            SignedInGamer.SignedIn += new EventHandler<SignedInEventArgs>(GamerSignedIn);
            SignedInGamer.SignedOut += new EventHandler<SignedOutEventArgs>(GamerSignedOut);
        }

        void GamerSignedIn(object sender, SignedInEventArgs e)
        {
            e.Gamer.Tag = new LocalPlayer(PlayerPresence.PlayerType.PLAYER_LOCAL, this);
            signedInPlayers.Add((PlayerPresence)(e.Gamer.Tag));
        }

        void GamerSignedOut(object sender, SignedOutEventArgs e)
        {
            RemovePlayer((PlayerPresence)(e.Gamer.Tag));
        }

        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            // Xbox 360 and most PCs support FourSamples/0 (4x) and TwoSamples/0 (2x)
            // antialiasing.
#if XBOX
            e.GraphicsDeviceInformation.PresentationParameters.MultiSampleQuality = 0;
            e.GraphicsDeviceInformation.PresentationParameters.MultiSampleType =
                MultiSampleType.FourSamples;
            return;
#endif
            int quality = 0;
            GraphicsAdapter adapter = e.GraphicsDeviceInformation.Adapter;
            SurfaceFormat format = adapter.CurrentDisplayMode.Format;
            // Check for 4xAA
            if (adapter.CheckDeviceMultiSampleType(DeviceType.Hardware, format,
                false, MultiSampleType.FourSamples, out quality))
            {
                // even if a greater quality is returned, we only want quality 0
                e.GraphicsDeviceInformation.PresentationParameters.MultiSampleQuality = 0;
                e.GraphicsDeviceInformation.PresentationParameters.MultiSampleType =
                    MultiSampleType.FourSamples;
            }
            // Check for 2xAA
            else if (adapter.CheckDeviceMultiSampleType(DeviceType.Hardware, format,
                false, MultiSampleType.TwoSamples, out quality))
            {
                // even if a greater quality is returned, we only want quality 0
                e.GraphicsDeviceInformation.PresentationParameters.MultiSampleQuality = 0;
                e.GraphicsDeviceInformation.PresentationParameters.MultiSampleType =
                    MultiSampleType.TwoSamples;
            }
            return;
        }

        /// <summary>
        /// Attempt to set the display mode to the desired resolution.  Itterates through the display
        /// capabilities of the default graphics adapter to determine if the graphics adapter supports the
        /// requested resolution.  If so, the resolution is set and the function returns true.  If not,
        /// no change is made and the function returns false.
        /// </summary>
        /// <param name="iWidth">Desired screen width.</param>
        /// <param name="iHeight">Desired screen height.</param>
        /// <param name="bFullScreen">True if you wish to go to Full Screen, false for Windowed Mode.</param>
        private bool InitGraphicsMode(int iWidth, int iHeight, bool bFullScreen)
        {
            // If we aren't using a full screen mode, the height and width of the window can
            // be set to anything equal to or smaller than the actual screen size.
            if (bFullScreen == false)
            {
                if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
                    && (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
                {
                    graphics.PreferredBackBufferWidth = iWidth;
                    graphics.PreferredBackBufferHeight = iHeight;
                    graphics.IsFullScreen = bFullScreen;
                    graphics.ApplyChanges();
                    GameConstants.SCREEN_HEIGHT = iHeight;
                    GameConstants.SCREEN_WIDTH = iWidth;
                    return true;
                }
            }
            else
            {
                // If we are using full screen mode, we should check to make sure that the display
                // adapter can handle the video mode we are trying to set.  To do this, we will
                // iterate thorugh the display modes supported by the adapter and check them against
                // the mode we want to set.
                foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                {
                    // Check the width and height of each mode against the passed values
                    if ((dm.Width == iWidth) && (dm.Height == iHeight))
                    {
                        // The mode is supported, so set the buffer formats, apply changes and return
                        graphics.PreferredBackBufferWidth = iWidth;
                        graphics.PreferredBackBufferHeight = iHeight;
                        graphics.IsFullScreen = bFullScreen;
                        graphics.ApplyChanges();
                        GameConstants.SCREEN_HEIGHT = iHeight;
                        GameConstants.SCREEN_WIDTH = iWidth;
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
            graphics.ApplyChanges();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            modelDatabase = new ModelDatabase(this);
            textureDatabase = new TextureDatabase(this);
            fontDatabase = new FontDatabase(this);
            audioDBManger = new AudioDatabase();

#if XBOX
            gameControls = new XBoxGameControls(this);
#else
            gameControls = new PCGameControls(this);
#endif
            networkManager = new NetworkManager(this);

            eventManager = new ManagerGameEvent(this);

            menuManager = new MenuManager(this);
            menuManager.SwitchMenu(MenuManager.eMenuTypes.MENU_MAIN);
            menuManager.SwitchMenu(MenuManager.eMenuTypes.MENU_SIGN_IN);

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            menuManager.Update(gameTime);
            if( gameManager != null && !gameControls.InMenu)
                gameControls.PollController(gameTime);
            gameControls.Update();

            //poolStick.Update(gameTime);

            if( networkManager != null )
                networkManager.Update(gameTime);

            if (gameManager != null)
                gameManager.Update(gameTime);

            audioDBManger.Update();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.RenderState.DepthBufferEnable = true;

            GraphicsDevice.RenderState.CullMode = CullMode.None;
            GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
            
            if (gameManager != null)
                gameManager.Draw(gameTime);
            
            menuManager.Draw(gameTime);
            
            if( networkManager != null )
                networkManager.Draw(gameTime);

            base.Draw(gameTime);
        }


        public void InitialiseGame()
        {
            InitialiseGame(false);
        }

        public void InitialiseGame(bool debug)
        {
            if (debug)
                gameManager = new DebugGameManager(this);
            else
                gameManager = new GameManager(this);

            gameControls.addListener(gameManager);

            if (debug)
                BeginGame(debug);
        }

        public void BeginGame()
        {
            BeginGame(false);
        }

        public void BeginGame(bool debug)
        {
            gameManager.StartGame();
        }

        public void EndGame()
        {
            bool isDebug = false;
            if (gameManager is DebugGameManager)
                isDebug = true;

            gameManager.EndGame();
            gameManager.Dispose();
            gameManager = null;

            if( isDebug == false )
                networkManager.Shutdown();
        }

        public void AddPlayer(PlayerPresence player)
        {
            signedInPlayers.Add(player);
        }

        public void RemovePlayer(PlayerPresence player)
        {
            signedInPlayers.Remove(player);
        }
    }
}
